﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;
using System.Drawing.Drawing2D;
using System.Collections.Specialized;

namespace CrayonsBox.DataModel
{
    public class Sprite : Drawing.Document.Drawable, IDataErrorInfo 
    {
        //+------------------------------------------------------------------------
        //| MODULE
        //+------------------------------------------------------------------------
        public class Module : Drawing.Document.Drawable
        {
            private BitmapSource m_bmpsrc;

            private TransformedBitmap m_thumb;

            private Bitmap m_cache;

            public Int32 ID { get; set; }

            public String Identifier
            {
                get 
                {
                    return ID.ToString("X4"); ; 
                }
            }

            public String Key { get; set; }
            
            public Int32 Image { get; set; }
            
            public override Point Location 
            {
                get
                {
                    return base.Location;
                }

                set
                {
                    if (!base.Location.Equals(value))
                    {
                        base.Location = value; //update for GUI
                    }
                }
            }         
            
            public override Size Dimension 
            {
                get
                {
                    return base.Dimension;
                }

                set
                {
                    if (!base.Dimension.Equals(value))
                    {
                        base.Dimension = value; // update for GUI
                    }
                }
            }

            public String Description { get; set; }

            public Bitmap Cache 
            {
                get
                {
                    return m_cache;
                }

                set
                {
                    m_cache = value;
                    NotifyPropertyChanged("Cache");
                }
            }

            public BitmapSource BmpSource 
            {
                get
                {
                    return m_bmpsrc;
                }

                set
                {
                    if (m_bmpsrc != value)
                    {
                        m_bmpsrc = value;
                        NotifyPropertyChanged("BmpSource");
                    }
                }
            }

            public TransformedBitmap Thumbnail 
            {
                get
                {
                    return m_thumb;
                }

                set
                {
                    if (m_thumb != value)
                    {
                        m_thumb = value;
                        NotifyPropertyChanged("Thumbnail");
                    }
                }
            }

            public Object UserData { get; set; }

            public Module() {}

            public override void OnDraw(Drawing.Document doc)
            {
                float[] dashValues = { 4, 1 };
                Pen blpen = new Pen(IsSelected ? Color.Red : Color.FromArgb(51, 153, 255), 1);
                blpen.DashPattern = dashValues;
                SolidBrush bbr = new SolidBrush(Color.FromArgb(51, 153, 255));

                Pen bkpen = new Pen(Color.Black, 1);
                SolidBrush wbr = new SolidBrush(Color.White);

                Rectangle tmpRec = new Rectangle(
                    (int)(Boundary.X * doc.ZoomRate),
                    (int)(Boundary.Y * doc.ZoomRate),
                    (int)(Boundary.Width * doc.ZoomRate),
                    (int)(Boundary.Height * doc.ZoomRate)
                );

                Matrix m = doc.Painter.Transform;

                doc.Painter.ResetTransform();

                doc.Painter.TranslateTransform(-doc.Viewport.X * doc.ZoomRate, -doc.Viewport.Y * doc.ZoomRate);

                doc.Painter.DrawLine(blpen, tmpRec.Left, tmpRec.Top, tmpRec.Right, tmpRec.Top);
                doc.Painter.DrawLine(blpen, tmpRec.Right, tmpRec.Top, tmpRec.Right, tmpRec.Bottom);
                doc.Painter.DrawLine(blpen, tmpRec.Right, tmpRec.Bottom, tmpRec.Left, tmpRec.Bottom);
                doc.Painter.DrawLine(blpen, tmpRec.Left, tmpRec.Bottom, tmpRec.Left, tmpRec.Top);

                //draw the sizing-grips if u want

                doc.Painter.Transform = m;
            }  
        }//___end MODULE


        //+------------------------------------------------------------------------
        //| FRAME
        //+------------------------------------------------------------------------
        public class InFrameModule : Drawing.Document.Drawable
        {
            protected Sprite m_host;

            protected Int32 m_moduleID;

            protected Bitmap m_cache;

            public Int32 ModuleID 
            {
                get 
                { 
                    return m_moduleID; 
                }

                set
                {
                    m_moduleID = value;
                    if (m_moduleID > -1 && m_moduleID < m_host.Modules.Count)
                    {
                        Cache = m_host.Modules[m_moduleID].Cache;
                        Boundary = new Rectangle(Location.X, Location.X, Cache.Width, Cache.Height);
                        
                        m_host.Modules[m_moduleID].PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
                        {
                            if (e.PropertyName.Equals("Cache"))
                            {
                                Boundary = new Rectangle(Location.X, Location.X, m_host.Modules[m_moduleID].Cache.Width, m_host.Modules[m_moduleID].Cache.Height);
                                Cache = m_host.Modules[m_moduleID].Cache;
                            }
                        };
                    }
                }
            }

            public Int32 PaletteID { get; set; }

            public Bitmap Cache
            {
                get
                {
                    return m_cache;
                }

                set
                {
                    m_cache = value;
                    NotifyPropertyChanged("Cache");
                }
            }

            public InFrameModule(Sprite host)
            {
                m_host = host;
            }

            public override void OnDraw(Drawing.Document doc)
            {
                if (m_host == null)
                    return;

                //Draw content of layer
                Matrix m = doc.Painter.Transform;

                doc.Painter.ResetTransform();

                Point localOrigin = Point.Empty;

                localOrigin.Offset((int)(-doc.Viewport.X * doc.ZoomRate), (int)(-doc.Viewport.Y * doc.ZoomRate));

                localOrigin.Offset((int)(Location.X * doc.ZoomRate), (int)(Location.Y * doc.ZoomRate));

                RectangleF srect = new RectangleF(-0.5f, -0.5f, Cache.Width, Cache.Height);
                RectangleF drect = new RectangleF(0, 0, Cache.Width, Cache.Height);

                doc.Painter.TranslateTransform(localOrigin.X, localOrigin.Y);

                doc.Painter.ScaleTransform(doc.ZoomRate, doc.ZoomRate);

                doc.Painter.DrawImage(Cache, drect, srect, GraphicsUnit.Pixel);

                doc.Painter.Transform = m;            
            }

        }//___end InFrameModule

        public class Frame : Drawing.Document.Drawable
        {
            private ObservableCollection<InFrameModule> m_inFrameModules;

            private BitmapSource m_bmpsrc;

            private TransformedBitmap m_thumb;
    
            public Int32 ID { get; set; }

            public override bool IsVisible
            {
                get
                {
                    return base.IsVisible;
                }
                set
                {
                    if(base.IsVisible != value)
                    {
                        base.IsVisible = value;
                        foreach (InFrameModule ifm in InFrameModules)
                            ifm.IsVisible = value;
                    }
                }
            }

            public Sprite SpriteHost { get; set; }

            public String Identifier
            {
                get
                {
                    return ID.ToString("X4");
                }
            }

            public Rectangle BoundingRect { get; set; }

            public Bitmap Cache { get; set; }

            public BitmapSource BmpSource
            {
                get
                {
                    return m_bmpsrc;
                }

                set
                {
                    if (m_bmpsrc != value)
                    {
                        m_bmpsrc = value;
                        NotifyPropertyChanged("BmpSource");
                    }
                }
            }

            public bool IsDirty { get; set; }

            public TransformedBitmap Thumbnail
            {
                get
                {
                    return m_thumb;
                }

                set
                {
                    if (m_thumb != value)
                    {
                        m_thumb = value;
                        NotifyPropertyChanged("Thumbnail");
                    }
                }
            }

            public ObservableCollection<InFrameModule> InFrameModules 
            {
                get
                {
                    return m_inFrameModules;
                }
            }

            public Frame()
            {
                m_inFrameModules = new ObservableCollection<InFrameModule>();
                m_inFrameModules.CollectionChanged += InFrameModules_CollectionChanged;
                BoundingRect = Rectangle.Empty;
            }

            public void Refresh()
            {
                if (m_inFrameModules.Count == 0) return;

                int l = m_inFrameModules[0].Location.X;
                int t = m_inFrameModules[0].Location.Y;
                int r = l + m_inFrameModules[0].Dimension.Width;
                int b = t + m_inFrameModules[0].Dimension.Height;

                foreach (InFrameModule ifm in m_inFrameModules)
                {
                    l = Math.Min(l, ifm.Location.X);
                    t = Math.Min(t, ifm.Location.Y);


                    r = Math.Max(r, ifm.Location.X + ifm.Dimension.Width);
                    b = Math.Max(b, ifm.Location.Y + ifm.Dimension.Height);
                }

                BoundingRect = Rectangle.FromLTRB(l, t, r, b);
            }

            private void InFrameModules_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    foreach (InFrameModule ifm in e.NewItems)
                    {
                        ifm.PropertyChanged += InFrameModule_PropertyChanged;
                    }
                    Refresh();
                }
            }

            private void InFrameModule_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName.Equals("Location") 
                    || e.PropertyName.Equals("Dimension") 
                    || e.PropertyName.Equals("Cache"))
                {
                    Refresh();
                }

                if (e.PropertyName.Equals("Cache"))
                {
                    SpriteHost.CacheFrame(this.ID);
                }
                this.IsDirty = true;
            }

            public override void OnDraw(Drawing.Document doc)
            {
                float[] dashValues = { 4, 1 };
                Pen blpen = new Pen(IsSelected ? Color.Red : Color.FromArgb(51, 153, 255), 1);
                blpen.DashPattern = dashValues;
                SolidBrush bbr = new SolidBrush(Color.FromArgb(51, 153, 255));

                Pen bkpen = new Pen(Color.Black, 1);
                SolidBrush wbr = new SolidBrush(Color.White);

                Rectangle tmpRec = new Rectangle(
                    (int)(BoundingRect.X * doc.ZoomRate),
                    (int)(BoundingRect.Y * doc.ZoomRate),
                    (int)(BoundingRect.Width * doc.ZoomRate),
                    (int)(BoundingRect.Height * doc.ZoomRate)
                );

                Matrix m = doc.Painter.Transform;

                doc.Painter.ResetTransform();

                doc.Painter.TranslateTransform(-doc.Viewport.X * doc.ZoomRate, -doc.Viewport.Y * doc.ZoomRate);

                doc.Painter.DrawLine(blpen, tmpRec.Left, tmpRec.Top, tmpRec.Right, tmpRec.Top);
                doc.Painter.DrawLine(blpen, tmpRec.Right, tmpRec.Top, tmpRec.Right, tmpRec.Bottom);
                doc.Painter.DrawLine(blpen, tmpRec.Right, tmpRec.Bottom, tmpRec.Left, tmpRec.Bottom);
                doc.Painter.DrawLine(blpen, tmpRec.Left, tmpRec.Bottom, tmpRec.Left, tmpRec.Top);

                //draw the sizing-grips if u want
                foreach (InFrameModule ifm in InFrameModules)
                {
                    if (ifm.IsSelected)
                    {
                        doc.Painter.DrawRectangle(new Pen(Color.Red, 1), ifm.Boundary);
                    }
                    else
                    {
                        float[] dash_border = { 2, 2 };
                        Pen bkdashpen = new Pen(Color.Black, 1);
                        bkdashpen.DashPattern = dash_border;
                        doc.Painter.DrawRectangle(bkdashpen, ifm.Boundary);
                    }
                }

                doc.Painter.Transform = m;
            }
        }//___end Frame


        //+------------------------------------------------------------------------
        //| ANIMATION
        //+------------------------------------------------------------------------
        public class Animation : INotifyPropertyChanged
        {
            public class InAnimationFrame : INotifyPropertyChanged
            {
                private Point m_position;

                private Int32 m_duration;

                public Int32 LayerID { get; set; }

                public Point Position
                {
                    get
                    {
                        return m_position;
                    }

                    set
                    {
                        if (!m_position.Equals(value))
                        {
                            m_position = value;
                            NotifyPropertyChanged("Position");
                        }
                    }
                }

                public Int32 Duration
                {
                    get
                    {
                        return m_duration;
                    }

                    set
                    {
                        if (!m_duration.Equals(value))
                        {
                            m_duration = value;
                            NotifyPropertyChanged("Duration");
                        }
                    }
                }

                public Int32 FrameID { get; set; }

                public event PropertyChangedEventHandler PropertyChanged;

                private void NotifyPropertyChanged(String info)
                {
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(info));
                    }
                }
            }

            public Int32 ID { get; set; }

            public String Identifier
            {
                get { return ID.ToString("X4"); }
            }

            public Int32 ElapsedTime { get; set; }

            public Int32 CurrentFrame { get; set; }

            public Int32 FrameRate { get; set; }

            public ObservableCollection<InAnimationFrame> InAnimationFrames { get; set; }

            public event PropertyChangedEventHandler PropertyChanged;

            private void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }

            public Animation()
            {
                InAnimationFrames = new ObservableCollection<InAnimationFrame>();
                FrameRate = 20; //hard-coded
            }
        }


        //+------------------------------------------------------------------------
        //| IMAGE 
        //+------------------------------------------------------------------------
        public class Image : Drawing.Document.Drawable, INotifyPropertyChanged
        {
            private Bitmap m_originalBitmap;

            public Int32 ID { get; set; }
            
            public String Path { get; set; }
            
            public Size Size { get; set; }
            
            public String Format { get; set; }

            public Bitmap Picture 
            {
                get
                {
                    return m_originalBitmap;
                }
                set
                {
                    m_originalBitmap = value;
                    Size = value.Size;
                    Format = value.PixelFormat.ToString();
                }
            }

            public Object UserData { get; set; }

            public event PropertyChangedEventHandler PropertyChanged;

            public override void OnDraw(Drawing.Document doc)
            {
                try
                {
                    Matrix m = doc.Painter.Transform;

                    doc.Painter.ResetTransform();

                    Point localOrigin = Point.Empty;

                    localOrigin.Offset((int)(-doc.Viewport.X * doc.ZoomRate), (int)(-doc.Viewport.Y * doc.ZoomRate));

                    RectangleF srect = new RectangleF(-0.5f, -0.5f, Size.Width, Size.Height);
                    RectangleF drect = new RectangleF(0, 0, Size.Width, Size.Height);

                    doc.Painter.TranslateTransform(localOrigin.X, localOrigin.Y);

                    Matrix beforeScale = doc.Painter.Transform;

                    doc.Painter.ScaleTransform(doc.ZoomRate, doc.ZoomRate);

                    doc.Painter.DrawImage(m_originalBitmap, drect, srect, GraphicsUnit.Pixel);

                    doc.Painter.Transform = beforeScale;

                    doc.Painter.Transform = m;
                }
                catch (Exception ex) { /* Ignore exceptions */ }
            }
        }


        //+------------------------------------------------------------------------
        //| SPRITE 
        //+------------------------------------------------------------------------
        private static ObservableCollection<Sprite> s_sprites = new ObservableCollection<Sprite>();
        
        private static Int32 s_index = -1;
        
        public static Sprite ActiveItem
        {
            get
            {
                if (s_index == -1)
                    return null;
                
                return s_sprites[s_index];
            }
        }

        private ObservableCollection<Module> m_modules;

        private ObservableCollection<Frame> m_frames;

        private ObservableCollection<Animation> m_animations;

        public Int32 ID { get; set; }

        public Int32 CurrentAnimation { get; set; }

        public ObservableCollection<Module> Modules 
        {
            get
            {
                return m_modules;
            }
        }

        public ObservableCollection<Frame> Frames 
        {
            get
            {
                return m_frames;
            }
        }

        public ObservableCollection<Animation> Animations
        {
            get
            {
                return m_animations;
            }
        }

        public ObservableCollection<Image> Images { get; set; }

        public Sprite(Drawing.Document doc) : base(doc)
        {
            this.CurrentAnimation = -1;
            m_modules = new ObservableCollection<Module>();
            m_frames = new ObservableCollection<Frame>();
            m_animations = new ObservableCollection<Animation>();
            this.Images = new ObservableCollection<Image>();
            this.ID = s_sprites.Count;
            Sprite.s_sprites.Add(this);
            if (Sprite.s_index == -1) s_index = this.ID;
        }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }

        public string this[string columnName]
        {
            get { throw new NotImplementedException(); }
        }

        public bool LoadJSON(string file)
        {
            try
            {
                string jsonFullPath = Path.GetFullPath(file);

                string jsonFullDir = Path.GetDirectoryName(jsonFullPath);

                StreamReader str = new StreamReader(jsonFullPath);
                String content = str.ReadToEnd();

                Newtonsoft.Json.Linq.JObject jo = Newtonsoft.Json.Linq.JObject.Parse(content);

                // get PICTURES info
                Newtonsoft.Json.Linq.JArray pics = jo["PICTURES"] as Newtonsoft.Json.Linq.JArray;

                if (pics != null)
                {
                    foreach (var imgItem in pics)
                    {
                        Sprite.Image img = new Sprite.Image();
                        img.ID = (int) imgItem["id"];
                        img.Path = jsonFullDir + "\\" + imgItem["path"].ToString();
                        Images.Add(img);
                    }
                }


                // get MODULES info
                Newtonsoft.Json.Linq.JArray modules = jo["MODULES"] as Newtonsoft.Json.Linq.JArray;

                if (modules != null)
                {
                    foreach (var modItem in modules)
                    {
                        Sprite.Module mod = new Sprite.Module();
                        mod.ID = (int)modItem["id"];
                        mod.Image = (int)modItem["pid"];

                        Newtonsoft.Json.Linq.JArray rec = modItem["rectangle"] as Newtonsoft.Json.Linq.JArray;
                        if (rec == null) continue;
                        mod.Location = new Point((int)rec[0], (int)rec[1]);
                        mod.Dimension = new Size((int)rec[2], (int)rec[3]);

                        if (mod.ID > m_modules.Count)
                            mod.ID = m_modules.Count;

                        m_modules.Insert(mod.ID, mod);

                        CacheModule(Modules.Count - 1);
                    }
                }


                //get FRAMES info
                Newtonsoft.Json.Linq.JArray frames = jo["FRAMES"] as Newtonsoft.Json.Linq.JArray;

                if (frames != null)
                {
                    foreach (var frameItem in frames)
                    {
                        Sprite.Frame fr = new Sprite.Frame();
                        AddFrame(fr);
                        fr.SpriteHost = this;
                        fr.ID = (int)frameItem["id"];
                        Newtonsoft.Json.Linq.JArray inFrameModules = frameItem["modules"] as Newtonsoft.Json.Linq.JArray;
                        if (inFrameModules != null)
                        {
                            foreach (var ifmItem in inFrameModules)
                            {
                                Sprite.InFrameModule ifm = new Sprite.InFrameModule(this);
                                ifm.ModuleID = (int) ifmItem["mid"];
                                Newtonsoft.Json.Linq.JArray loc = ifmItem["location"] as Newtonsoft.Json.Linq.JArray;
                                if (loc == null) continue;
                                ifm.Location = new Point((int)loc[0], (int)loc[1]);
                                fr.InFrameModules.Add(ifm);
                            }
                        }                  
                        CacheFrame(Frames.Count - 1);
                    }
                }

                
                // get ANIMATIONS info
                Newtonsoft.Json.Linq.JArray anims = jo["ANIMATIONS"] as Newtonsoft.Json.Linq.JArray;

                if (anims != null)
                {
                    foreach (var animItem in anims)
                    {
                        Sprite.Animation an = new Sprite.Animation();
                        AddAnimation(an);
                        an.ID = (int) animItem["id"];
                        Newtonsoft.Json.Linq.JArray inAnimFrames = animItem["frames"] as Newtonsoft.Json.Linq.JArray;
                        
                        if (inAnimFrames != null)
                        {
                            foreach (var iafItem in inAnimFrames)
                            {
                                Sprite.Animation.InAnimationFrame iaf = new Sprite.Animation.InAnimationFrame();
                                iaf.FrameID = (Int32)iafItem["fid"];
                                iaf.Duration = (Int32)iafItem["sequence"];
                                Newtonsoft.Json.Linq.JArray loc = iafItem["location"] as Newtonsoft.Json.Linq.JArray;
                                if (loc == null) continue;
                                iaf.Position = new Point((int)loc[0], (int)loc[1]);
                                an.InAnimationFrames.Add(iaf);
                            }
                        }
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public bool LoadXML()
        {
            return false;
        }

        public bool LoadBIN()
        {
            return false;
        }

        public void AddModule(Module m)
        {
            m_modules.Add(m);
        }

        public void CacheModule(int idx)
        {
            if (idx >= Modules.Count)
                return;

            Module m = Modules[idx];

            if ((m.Dimension.Width * m.Dimension.Height).Equals(0))
                return;


            Bitmap newCache = new Bitmap(m.Dimension.Width, m.Dimension.Height, PixelFormat.Format32bppArgb);
            
            Graphics cg = Graphics.FromImage(newCache);

            RectangleF src = new RectangleF(m.Location.X, m.Location.Y, m.Dimension.Width, m.Dimension.Height);

            RectangleF dest = new RectangleF(0, 0, m.Dimension.Width, m.Dimension.Height);

            cg.SmoothingMode = SmoothingMode.None;
            cg.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            cg.CompositingMode = CompositingMode.SourceOver;
            cg.CompositingQuality = CompositingQuality.AssumeLinear;
            cg.InterpolationMode = InterpolationMode.NearestNeighbor;

            cg.DrawImage(Images[m.Image].Picture, dest, src, GraphicsUnit.Pixel);

            cg.Dispose();

            m.Cache = newCache;

            BitmapSource bs = Drawing.Utils.CreateBitmapSourceFromBitmap(m.Cache);

            double rate = Math.Min(96.0f / bs.PixelWidth, 96.0f / bs.PixelHeight);

            if (bs.PixelWidth <= 96 && bs.PixelHeight <= 96)
                rate = 1.0f;

            TransformedBitmap tb = new TransformedBitmap();
            tb.BeginInit();
            tb.Source = bs;
            tb.Transform = new System.Windows.Media.ScaleTransform(rate, rate);
            tb.EndInit();

            m.Thumbnail = tb;
            m.BmpSource = bs;
        }

        public void AddFrame(Frame fr)
        {
            fr.SpriteHost = this;
            m_frames.Add(fr);
        }

        public void CacheFrame(int idx)
        {
            if (idx >= m_frames.Count)
                return;

            Frame fr = Frames[idx];

            if (fr.BoundingRect.Width * fr.BoundingRect.Height == 0)
                return;

            Bitmap newCache = new Bitmap(fr.BoundingRect.Width, fr.BoundingRect.Height, PixelFormat.Format32bppArgb);

            Graphics cg = Graphics.FromImage(newCache);

            cg.SmoothingMode = SmoothingMode.None;
            cg.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            cg.CompositingMode = CompositingMode.SourceOver;
            cg.CompositingQuality = CompositingQuality.AssumeLinear;
            cg.InterpolationMode = InterpolationMode.NearestNeighbor;

            foreach (InFrameModule ifm in fr.InFrameModules.Reverse())
            {
                RectangleF src = new RectangleF(-0.5f, -0.5f, ifm.Cache.Width, ifm.Cache.Height);

                RectangleF dest = new RectangleF(ifm.Location.X - fr.BoundingRect.X, ifm.Location.Y - fr.BoundingRect.Y, 
                    ifm.Cache.Width, ifm.Cache.Height);

                cg.DrawImage(ifm.Cache, dest, src, GraphicsUnit.Pixel);
            }

            cg.Dispose();

            fr.Cache = newCache;

            BitmapSource bs = Drawing.Utils.CreateBitmapSourceFromBitmap(fr.Cache);

            double rate = Math.Min(96.0f / bs.PixelWidth, 96.0f / bs.PixelHeight);

            if (bs.PixelWidth <= 96 && bs.PixelHeight <= 96)
                rate = 1.0f;

            TransformedBitmap tb = new TransformedBitmap();
            tb.BeginInit();
            tb.Source = bs;
            tb.Transform = new System.Windows.Media.ScaleTransform(rate, rate);
            tb.EndInit();

            fr.Thumbnail = tb;
            fr.BmpSource = bs;

            fr.IsDirty = false;
        }

        public void AddAnimation(Animation an)
        {
            m_animations.Add(an);
        }

        public override void Draw()
        {
            if (!IsVisible)
                return;

            Matrix m = Doc.Painter.Transform;

            Animation.InAnimationFrame fr = Animations[CurrentAnimation].InAnimationFrames[Animations[CurrentAnimation].CurrentFrame];

            Doc.Painter.ResetTransform();

            Point localOrigin = Point.Empty;

            localOrigin.Offset((int)(-Doc.Viewport.X * Doc.ZoomRate), (int)(-Doc.Viewport.Y * Doc.ZoomRate));

            localOrigin.Offset((int)(fr.Position.X * Doc.ZoomRate), (int)(fr.Position.Y * Doc.ZoomRate));

            Doc.Painter.TranslateTransform(localOrigin.X, localOrigin.Y);

            RectangleF srect = new RectangleF(-0.5f, -0.5f, Frames[fr.FrameID].Cache.Width, Frames[fr.FrameID].Cache.Height);
            RectangleF drect = new RectangleF(0, 0, Frames[fr.FrameID].Cache.Width, Frames[fr.FrameID].Cache.Height);

            Doc.Painter.DrawImage(Frames[fr.FrameID].Cache, drect, srect, GraphicsUnit.Pixel);

            Doc.Painter.Transform = m;
        }

        public override void OnUpdate(int elapsedMs)
        {
            bool moveToNextFrame = false;

            Animation.InAnimationFrame fr = Animations[CurrentAnimation].InAnimationFrames[Animations[CurrentAnimation].CurrentFrame];

            Animations[CurrentAnimation].ElapsedTime += elapsedMs;

            int mpf = 1000 / Animations[CurrentAnimation].FrameRate;

            if (Animations[CurrentAnimation].ElapsedTime > fr.Duration * mpf)
            {
                Animations[CurrentAnimation].ElapsedTime -= fr.Duration * mpf;
                moveToNextFrame = true;
            }

            if (moveToNextFrame)
            {
                if (Animations[CurrentAnimation].CurrentFrame + 1 < Animations[CurrentAnimation].InAnimationFrames.Count)
                    Animations[CurrentAnimation].CurrentFrame++;
                else
                    Animations[CurrentAnimation].CurrentFrame = 0;
            }
        }
    }
}
